<?php

namespace App\Service\Kms\Auth;

use AlibabaCloud\SDK\Dyvmsapi\V20170525\Models\ListHotlineTransferNumberResponseBody\data\values;
use App\Enums\Feishu\AppEnum;
use App\Enums\Kms\UserErrorEnum;
use App\Error\Kms\UserError;
use App\Exceptions\Custom\ResponseHttpException;
use App\Exceptions\KfHttpResponseException;
use App\Http\Middleware\KmsAuthenticate;
use App\Models\Kms\User\KmsUser;
use App\Models\Kms\User\KmsUserLoginHistoryModel;
use App\Models\Kms\User\User;
use App\Remote\Feishu\Sso\SsoRemote;
use App\Remote\Rms\RmsSmsRemote;
use App\Service\CallingCards\CardService;
use App\Service\Kms\KmsBaseService;
use App\Service\OpenShop\Feishu\ConfigService;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Config\Repository as Config;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Kuafu\IpWhite\Service\IpWhiteService;
use Laravel\Passport\Passport;
use Laravel\Passport\RefreshTokenRepository;
use Laravel\Passport\TokenRepository;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use League\OAuth2\Server\CryptKey;

class AuthService extends KmsBaseService
{

    /**
     * @param object $params
     * @param string $ip
     * @return mixed
     * @throws GuzzleException
     * @throws ValidationException|BindingResolutionException
     */
    public static function fsLogin(object $params, string $ip)
    {
        $userinfo = SsoRemote::userinfo($params->code, AppEnum::APP_TYPE_KMS);
//        $userinfo = ["mobile" => "+8613478370546"];


        list($token, $uid) = self::getToken($userinfo["mobile"], $ip);

        if (version_compare($params->appVersion, "1.1.2", ">")) {
            KmsUserLoginHistoryModel::query()->insertGetId([
                "userId"             => $uid,
                "userAgent"          => (string)attributeGet($params, "userAgent", ""),
                "ip"                 => (string)$ip,
                "appVersion"         => (string)$params->appVersion,
                "longitude"          => $params->longitude,
                "latitude"           => $params->latitude,
                "deviceModel"        => (string)$params->deviceModel,
                "deviceSystem"       => (string)$params->deviceSystem,
                "deviceSerialNumber" => (string)$params->deviceSerialNumber,
                "createdAt"          => time(),
                "createdByUid"       => $uid
            ]);
        }

        return $token;
    }

    /**
     * @param string $mobile
     * @param string $ip
     * @return array
     * @throws BindingResolutionException
     */
    public static function getToken(string $mobile, string $ip): array
    {
        $mobile       = str_replace("+86", "", $mobile);
        $kmsUserGroup = KmsUser::query()->where("mobile", $mobile)->get();

        if ($kmsUserGroup->isEmpty()) {
            throw new KfHttpResponseException(UserError::USER_NOT_EXISTS_IN_FS);
        }
        if (!$kmsUserGroup->where("rmsUserId", ">", 0)->count()) {
            throw new KfHttpResponseException(UserError::USER_NOT_IN_SYSTEM);
        }
        if (!$kmsUserGroup->where("isExited", 0)->count()) {
            throw new KfHttpResponseException(UserError::USER_EXITED);
        }

        $kmsUser           = $kmsUserGroup->sortByDesc("id")->first();
        $oauthClientsModel = Passport::clientModel()::where("name", "kms")->first();
        request()->request->add([
            'grant_type'    => 'password',
            'client_id'     => $oauthClientsModel->id,
            'client_secret' => $oauthClientsModel->secret,
            'username'      => $mobile,
            'password'      => '',
            'scope'         => '*',
        ]);

        $proxy = Request::create(
            'oauth/token',
            'POST'
        );

        $tokenRepository        = app(TokenRepository::class);
        $refreshTokenRepository = app(RefreshTokenRepository::class);

        // 需要爬我们接口去取数,临时取消指定 userId 的登录提出功能
        // 5841 鬼鲛
        if (!in_array($kmsUser->rmsUserId, [5841])) {
            Passport::tokenModel()::where("user_id", $kmsUser->rmsUserId)->get()->each(function ($row) use (
                $tokenRepository,
                $refreshTokenRepository
            ) {
                $tokenRepository->revokeAccessToken($row->id);
                $refreshTokenRepository->revokeRefreshTokensByAccessTokenId($row->id);
            });
        }

        $token = json_decode(Route::dispatch($proxy)->getContent(), true);

        if (array_key_exists("access_token", $token)) {
            //重写jwt-token claims
            $parsedToken = (new Parser())->parse($token["access_token"]);
            $claims      = $parsedToken->getClaims();
            $builder     = new Builder();
            collect($claims)->merge([
                "ip"      => (string)$ip,
                'user_id' => (string)$kmsUser->rmsUserId,
                'type'    => KmsAuthenticate::IP_WHITE_TYPE_KMS,
            ])->each(function ($claim, $key) use ($builder) {
                $val = is_string($claim) ? $claim : $claim->getValue();
                switch ($key) {
                    case "aud":
                        $builder->permittedFor($val);
                        break;
                    case "jti":
                        $builder->identifiedBy($val);
                        break;
                    case "iat":
                        $builder->issuedAt($val);
                        break;
                    case "nbf":
                        $builder->canOnlyBeUsedAfter($val);
                        break;
                    case "exp":
                        $builder->expiresAt($val);
                        break;
                    case "sub":
                        $builder->relatedTo($val);
                        break;
                    default:
                        $builder->withClaim($key, $val);
                        break;
                }
            });
            $key = str_replace('\\n', "\n", app()->make(Config::class)->get('passport.private_key'));
            if (!$key) {
                $key = 'file://' . Passport::keyPath('oauth-private.key');
            }
            $privateKey            = new CryptKey($key, null, false);
            $token["access_token"] = (string)$builder->getToken(
                new Sha256(),
                new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase())
            );

            // 白名单登录
            (new IpWhiteService($kmsUser->rmsUserId, KmsAuthenticate::IP_WHITE_TYPE_KMS, null))->login($ip);
        }
        return [$token, $kmsUser->rmsUserId];
    }

    /**
     * @param object $params
     * @param string $ip
     * @return mixed|void
     * @throws Exception
     */
    public static function virtualLogin(object $params, string $ip)
    {
        try {
            $virtualLogin = ConfigService::getConfigDetail("kms.auth", "virtualLogin");
            if ($virtualLogin["value"]["version"] != $params->appVersion) {
                throw new KfHttpResponseException(UserError::ERROR_APP_VERSION);
            }

            $exists = collect($virtualLogin["value"]["account"])->where("mobile", $params->mobile)
                                                                ->where("password", $params->password)->first();

            if (!$exists) {
                throw new KfHttpResponseException(UserError::ERROR_USER_OR_PASSWORD);
            }

            list($token, $uid) = self::getToken($params->mobile, request()->ip());

            if (version_compare($params->appVersion, "1.1.2", ">")) {
                KmsUserLoginHistoryModel::query()->insertGetId([
                    "userId"             => $uid,
                    "userAgent"          => (string)attributeGet($params, "userAgent", ""),
                    "ip"                 => (string)$ip,
                    "appVersion"         => (string)$params->appVersion,
                    "longitude"          => $params->longitude,
                    "latitude"           => $params->latitude,
                    "deviceModel"        => (string)$params->deviceModel,
                    "deviceSystem"       => (string)$params->deviceSystem,
                    "deviceSerialNumber" => (string)$params->deviceSerialNumber,
                    "createdAt"          => time(),
                    "createdByUid"       => $uid
                ]);
            }

            return $token;
        } catch (Exception $exception) {
            handleKfException($exception);
        }
    }


    /**
     * @param object $params
     * @return Application|RedirectResponse|Redirector
     */
    public static function code(object $params)
    {
        request()->session()->put('state', $state = Str::random(40));
        $client = Passport::clientModel()::where("id", $params->clientId)->first();
        $query  = http_build_query([
            'client_id'     => $params->clientId,
            'redirect_uri'  => $client->redirect,
            'response_type' => 'code',
            'scope'         => '',
            'state'         => $state,
        ]);

        return redirect(vsprintf("%s%s%s", [config("app.url"), "oauth/authorize?", $query]));
    }

    /**
     * @param object $params
     * @return mixed
     */
    public static function token(object $params)
    {
        $client = Passport::clientModel()::where("id", $params->clientId)->first();
        request()->request->add([
            'grant_type'    => 'authorization_code',
            'client_id'     => $client->id,
            'client_secret' => $client->secret,
            'code'          => $params->code,
            'redirect_uri'  => null
        ]);

        $proxy = Request::create(
            'oauth/token',
            'POST'
        );

        $response = json_decode(Route::dispatch($proxy)->getContent(), true);
        if (!array_key_exists("error", $response)) {
            $response["token"] = vsprintf("%s %s", [$response["token_type"], $response["access_token"]]);
        }
        return $response;
    }

    /**
     * 登出
     */
    public static function logout()
    {
        $tokenRepository        = app(TokenRepository::class);
        $refreshTokenRepository = app(RefreshTokenRepository::class);
        Passport::tokenModel()::where("user_id", auth()->user()->rmsUserId)->get()->each(function ($row) use (
            $tokenRepository,
            $refreshTokenRepository
        ) {
            $tokenRepository->revokeAccessToken($row->id);
            $refreshTokenRepository->revokeRefreshTokensByAccessTokenId($row->id);
        });
    }

    /**
     * 用户登录注册-审核用
     * @param string $mobile
     * @param string $code
     * @param string $ip
     * @param string $appVersion
     * @return mixed|void
     * @throws Exception|GuzzleException
     */
    public static function mobileLogin(string $mobile, string $code, string $ip, string $appVersion)
    {
        try {
            $virtualLogin = ConfigService::getConfigDetail("kms.auth", "virtualLogin", 5 * 60);
            if ($virtualLogin["value"]["version"] != $appVersion || !$virtualLogin["value"]["switch"]) {
                throw new KfHttpResponseException(UserError::APP_VERSION_ERR);
            }

            $verifyCodeRes = RmsSmsRemote::verifyCodeByMobile($mobile, $code);
            if ($verifyCodeRes && $verifyCodeRes['code'] != 200) {
                throw new KfHttpResponseException(UserError::VERIFY_CODE_ERR);
            }
            $user = User::query()->where('mobile', $mobile)->first();
            if (!$user) { //新建用户
                User::add('test' . rand(1, 100), $mobile,
                    'https://file.51kuafu.com/12/61/d4922f315b8401da42eb17f0323e422bc1e1061e11bb6e6576307301f2eb.jpeg');
            }

            list($token, $_) = self::getToken($virtualLogin["value"]["account"][0]['mobile'], $ip);
            Log::info(
                '用户登录注册-审核用', [
                    'mobile'     => $mobile,
                    'code'       => $code,
                    'ip'         => $ip,
                    'appVersion' => $appVersion,
                ]
            );
            return $token;
        } catch (Exception $exception) {
            handleKfException($exception);
        }
    }
}
